#include "Sampler.h"

void Sampler::shuffle_X_Coordinates() {
	for (int p = 0; p < numSamples; p++)
		for (int i = 0; i < numSamples - 1; i++) {
			int target = RANDINT % numSamples + p * numSamples;
			SWAP(samples[i + p * numSamples + 1].x, samples[target].x);
		}
}

void Sampler::shuffle_Y_Coordinates() {
	for (int p = 0; p < numSamples; p++)
		for (int i = 0; i < numSamples - 1; i++) {
			int target = RANDINT % numSamples + p * numSamples;
			SWAP(samples[i + p * numSamples + 1].y, samples[target].y);
		}
}

void Sampler::setupSamples() {
	generateSamples();
	setupShuffledIndices();
}

void Sampler::tentFilter() {
	int size = samples.size();
	Vector2 sp;
	for (int i = 0; i < size; i++) {
		sp = samples[i];
		
		if (sp.x < 0.5)
			samples[i].x = sqrt(sp.x);
		else
			samples[i].x = 1.0 - sqrt(1.0 - sp.x);

		if (sp.y < 0.5)
			samples[i].y = sqrt(sp.y);
		else
			samples[i].y = 1.0 - sqrt(1.0 - sp.y);
	}
}

void Sampler::cubicFilter() {
	int size = samples.size();

	for (int i = 0; i < size; i++) {
		samples[i].x = (MathUtils::cubicFilter(samples[i].x) + 1) / 2.0;
		samples[i].y = (MathUtils::cubicFilter(samples[i].y) + 1) / 2.0;
	}
}

void Sampler::mapSamplesToUnitDisk() {
	int size = samples.size();
	Double r, phi;		// polar coordinates
	Vector2 sp; 		// sample point on unit disk

	diskSamples.reserve(size);

	for (int j = 0; j < size; j++) {
		// map sample point to [-1, 1] X [-1,1]
		sp.x = 2.0 * samples[j].x - 1.0;
		sp.y = 2.0 * samples[j].y - 1.0;

		if (sp.x > -sp.y) {			// sectors 1 and 2
			if (sp.x > sp.y) {		// sector 1
				r = sp.x;
				phi = sp.y / sp.x;
			} else {					// sector 2
				r = sp.y;
				phi = 2 - sp.x / sp.y;
			}
		} else {						// sectors 3 and 4
			if (sp.x < sp.y) {		// sector 3
				r = -sp.x;
				phi = 4 + sp.y / sp.x;
			} else {					// sector 4
				r = -sp.y;
				if (sp.y != 0.0)	// avoid division by zero at origin
					phi = 6 - sp.x / sp.y;
				else
					phi = 0.0;
			}
		}
		phi *= PI / 4.0;
		diskSamples.emplace_back(r * cos(phi), r * sin(phi));
	}
	//samples.erase(samples.begin(), samples.end());
}

// Maps the 2D sample points to 3D points on a unit hemisphere with a cosine power
// density distribution in the polar angle
void Sampler::mapSamplesToHemisphere(Double exp) {
	int size = samples.size();
	hemisphereSamples.reserve(size);

	for (int j = 0; j < size; j++) {
		Double cos_phi = cos(2.0 * PI * samples[j].x);
		Double sin_phi = sin(2.0 * PI * samples[j].x);
		Double cos_theta = pow((1.0 - samples[j].y), 1.0 / (exp + 1.0));
		Double sin_theta = sqrt(1.0 - cos_theta * cos_theta);
		Double pu = sin_theta * cos_phi;
		Double pv = sin_theta * sin_phi;
		Double pw = cos_theta;
		hemisphereSamples.emplace_back(pu, pv, pw);
	}
}

void Sampler::mapSamplesToSqhere() {
	Double r1, r2;
	Double x, y, z;
	Double r, phi;

	int size = samples.size();
	sphereSamples.reserve(size);

	for (int j = 0; j < size; j++) {
		r1 = samples[j].x;
		r2 = samples[j].y;
		z = 1.0 - 2.0 * r1;
		r = sqrt(1.0 - z * z);
		phi = 2 * PI * r2;
		x = r * cos(phi);
		y = r * sin(phi);
		sphereSamples.emplace_back(x, y, z);
	}
}

void Sampler::setupShuffledIndices() {
	shuffledIndices.reserve(numSamples * numSets);
	vector<int> indices;

	for (int j = 0; j < numSamples; j++)
		indices.push_back(j);

	for (int p = 0; p < numSets; p++) {
		random_shuffle(indices.begin(), indices.end());
		for (int j = 0; j < numSamples; j++)
			shuffledIndices.push_back(indices[j]);
	}
}

Vector2 Sampler::sampleUnitSquare() {
	nextSampleSetIfNeeded();
	return samples[jump + shuffledIndices[jump + count++ % numSamples]];
}

Vector2 Sampler::sampleUnitDisk() {
	nextSampleSetIfNeeded();
	return diskSamples[jump + shuffledIndices[jump + count++ % numSamples]];
}

Vector3 Sampler::sampleHemisphere() {
	nextSampleSetIfNeeded();
	return hemisphereSamples[jump + shuffledIndices[jump + count++ % numSamples]];
}

Vector3 Sampler::sampleSqhere() {
	nextSampleSetIfNeeded();
	return sphereSamples[jump + shuffledIndices[jump + count++ % numSamples]];
}

Vector2 Sampler::sampleOneSet() {
	return samples[jump + shuffledIndices[jump + count++ % numSamples]];
}

Vector2 Sampler::sampleUnitSquare(int setIndex, int sampleIndex) {
	setIndex = getSampleSet(setIndex);
	return samples[setIndex + shuffledIndices[setIndex + sampleIndex % numSamples]];
}

Vector2 Sampler::sampleUnitDisk(int setIndex, int sampleIndex) {
	setIndex = getSampleSet(setIndex);
	return diskSamples[setIndex + shuffledIndices[setIndex + sampleIndex % numSamples]];
}

/*
Vector3 Sampler::sampleHemisphere(int setIndex, int sampleIndex) {
	setIndex = getSampleSet(setIndex);
	return hemisphereSamples[setIndex + shuffledIndices[setIndex + sampleIndex % numSamples]];
}

Vector3 Sampler::sampleSqhere(int setIndex, int sampleIndex) {
	setIndex = getSampleSet(setIndex);
	return sphereSamples[setIndex + shuffledIndices[setIndex + sampleIndex % numSamples]];
}
*/